﻿using System;
using System.Globalization;
using System.Reactive;
using System.Threading.Tasks;
using Avalonia.Threading;
using ReactiveUI;

namespace Question3.ViewModels;

public class MainWindowViewModel : ViewModelBase
{
        private string _rawHex;
        public string RawHex
        {
            get => _rawHex;
            set => this.RaiseAndSetIfChanged(ref _rawHex, value);
        }

        private string _number;
        public string Number
        {
            get => _number;
            set => this.RaiseAndSetIfChanged(ref _number, value);
        }

        public ReactiveCommand<Unit, Unit> HexToDoubleCommand { get; }
        public ReactiveCommand<Unit, Unit> HexToInt32Command { get; }
        public ReactiveCommand<Unit, Unit> HexToInt64Command { get; }
        public ReactiveCommand<Unit, Unit> HexToFloatCommand { get; }
        public ReactiveCommand<Unit, Unit> DoubleToHexCommand { get; }
        public ReactiveCommand<Unit, Unit> Int32ToHexCommand { get; }
        public ReactiveCommand<Unit, Unit> Int64ToHexCommand { get; }
        public ReactiveCommand<Unit, Unit> FloatToHexCommand { get; }

        public MainWindowViewModel()
        {
            HexToDoubleCommand = ReactiveCommand.CreateFromTask(HexToDouble);
            HexToInt32Command = ReactiveCommand.CreateFromTask(HexToInt32);
            HexToInt64Command = ReactiveCommand.CreateFromTask(HexToInt64);
            HexToFloatCommand = ReactiveCommand.CreateFromTask(HexToFloat);
            DoubleToHexCommand = ReactiveCommand.CreateFromTask(DoubleToHex);
            Int32ToHexCommand = ReactiveCommand.CreateFromTask(Int32ToHex);
            Int64ToHexCommand = ReactiveCommand.CreateFromTask(Int64ToHex);
            FloatToHexCommand = ReactiveCommand.CreateFromTask(FloatToHex);
        }

        private async Task HexToDouble()
        {
            if (TryParseHexToBytes(RawHex, out var bytes) && bytes.Length == 8)
            {
                var result = BitConverter.ToDouble(bytes, 0);
                await UpdateNumber(result.ToString());
            }
        }

        private async Task HexToInt32()
        {
            if (TryParseHexToBytes(RawHex, out var bytes) && bytes.Length == 4)
            {
                var result = BitConverter.ToInt32(bytes, 0);
                await UpdateNumber(result.ToString());
            }
        }

        private async Task HexToInt64()
        {
            if (TryParseHexToBytes(RawHex, out var bytes) && bytes.Length == 8)
            {
                var result = BitConverter.ToInt64(bytes, 0);
                await UpdateNumber(result.ToString());
            }
        }

        private async Task HexToFloat()
        {
            if (TryParseHexToBytes(RawHex, out var bytes) && bytes.Length == 4)
            {
                var result = BitConverter.ToSingle(bytes, 0);
                await UpdateNumber(result.ToString());
            }
        }

        private async Task DoubleToHex()
        {
            if (double.TryParse(Number, out var value))
            {
                var bytes = BitConverter.GetBytes(value);
                await UpdateRawHex(BytesToHexString(bytes));
            }
        }

        private async Task Int32ToHex()
        {
            if (int.TryParse(Number, out var value))
            {
                var bytes = BitConverter.GetBytes(value);
                await UpdateRawHex(BytesToHexString(bytes));
            }
        }

        private async Task Int64ToHex()
        {
            if (long.TryParse(Number, out var value))
            {
                var bytes = BitConverter.GetBytes(value);
                await UpdateRawHex(BytesToHexString(bytes));
            }
        }

        private async Task FloatToHex()
        {
            if (float.TryParse(Number, out var value))
            {
                var bytes = BitConverter.GetBytes(value);
                await UpdateRawHex(BytesToHexString(bytes));
            }
        }

        private async Task UpdateNumber(string value)
        {
            await Dispatcher.UIThread.InvokeAsync(() =>
            {
                Number = value;
            });
        }

        private async Task UpdateRawHex(string value)
        {
            await Dispatcher.UIThread.InvokeAsync(() =>
            {
                RawHex = value;
            });
        }

        private static bool TryParseHexToBytes(string hex, out byte[] bytes)
        {
            hex = hex.Replace(" ", "");
            bytes = new byte[hex.Length / 2];
            for (var i = 0; i < hex.Length; i += 2)
            {
                if (!byte.TryParse(hex.AsSpan(i, 2), System.Globalization.NumberStyles.HexNumber, null, out bytes[i / 2]))
                {
                    return false;
                }
            }
            return true;
        }

        private static string BytesToHexString(byte[] bytes)
        {
            return BitConverter.ToString(bytes).Replace("-", " ");
        }
}